package com.example.demo.module.repository.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.demo.common.entity.enums.RepositoryTypeEnum;
import com.example.demo.common.entity.response.ResCode;
import com.example.demo.common.exception.CommonException;
import com.example.demo.common.service.impl.BaseServiceImpl;
import com.example.demo.constant.Constant;
import com.example.demo.entity.pojo.repository.MaterialPoJo;
import com.example.demo.entity.pojo.repository.RepositoryPoJo;
import com.example.demo.entity.request.repository.RepositoryRequest;
import com.example.demo.module.repository.dto.TreeNode;
import com.example.demo.module.repository.mapper.RepositoryMapper;
import com.example.demo.module.repository.service.MaterialService;
import com.example.demo.module.repository.service.RepositoryService;
import com.example.demo.utils.CommonUtils;
import com.example.demo.utils.EnumUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * .
 *
 * @author Mei Ruoxiao
 * @since 2020/9/11
 */
@Service
@Slf4j
public class RepositoryServiceImpl extends BaseServiceImpl<RepositoryMapper, RepositoryPoJo> implements RepositoryService {
    @Resource
    private MaterialService materialService;

    @Override
    public long create(RepositoryRequest request, long companyId) {
        log.info("create:[request:{}, companyId:{}]", request, companyId);
        String name = request.getName();
        long parentId = request.getParentId();
        int type = request.getType();
        RepositoryPoJo nameF = getByName(name, parentId, type, companyId);
        if (!Objects.isNull(nameF)) {
            throw CommonException.of(ResCode.REPOSITORY_NAME_DUPLICATE);
        }
        // 父ID验证
        RepositoryPoJo find;
        if (CommonUtils.isErrorId(parentId)) {
            find = getRootFolder(type, companyId);
        } else {
            find = getById(parentId);
            if (Objects.isNull(find)) {
                throw CommonException.of(ResCode.REPOSITORY_PARENT_NOT_EXIST);
            }
        }
        RepositoryPoJo poJo = RepositoryPoJo.of(name, type, find.getId(), companyId);
        create(poJo);
        long id = poJo.getId();
        log.info("create:新增文件夹成功[id:{}]", id);
        RepositoryPoJo repository = getById(id);
        repository.setFullIds(find.getFullIds() + id + ",");
        updateById(repository);
        log.info("create:修改fullIds字段成功[fullIds:{}]", repository.getFullIds());
        return id;
    }

    @Override
    public TreeNode tree(int type, long companyId) {
        log.info("tree:[type:{}, companyId:{}]", type, companyId);
        RepositoryPoJo poJo = getRootFolder(type, companyId);
        TreeNode root = TreeNode.of(poJo);
        log.info("tree:取得根目录[treeNode:{}]", JSON.toJSONString(root));
        // 获取该类型的所有文件夹
        List<RepositoryPoJo> list = list(
                Wrappers.<RepositoryPoJo>lambdaQuery()
                        .eq(RepositoryPoJo::getCompanyId, companyId)
                        .eq(RepositoryPoJo::getType, type)
                        .orderByDesc(RepositoryPoJo::getCreateTime));
        log.info("tree:获取所有文件夹列表文件夹总数[total:{}]", list.size());
        // POJO转Node
        List<TreeNode> nodeList = getNodeList(list);
        nodeList.remove(root);
        // 递归获取子文件夹
        log.info("tree:移除根目录之后的文件夹列表[nodeList:{}]", nodeList);
        TreeNode treeNode = getChildren(nodeList, root);
        return treeNode;
    }


    @Override
    public RepositoryPoJo getByName(String name, long parentId, int type, long companyId) {
        return null;
    }

    @Override
    public RepositoryPoJo getRootFolder(int type, long companyId) {
        RepositoryPoJo find = this.getOne(Wrappers.<RepositoryPoJo>lambdaQuery()
                .eq(RepositoryPoJo::getType, type)
                .eq(RepositoryPoJo::getCompanyId, companyId)
                .eq(RepositoryPoJo::getParentId, Constant.SYSTEM_ID));
        if (Objects.nonNull(find)) {
            log.info("getRootFolder:根目录不为空[companyId:{},type:{}]", companyId, type);
            return find;
        }
        log.info("getRootFolder:根目录为空需要创建[companyId:{},type:{}]", companyId, type);
        long id = createRoot(type, companyId);
        return getById(id);
    }

    @Override
    public List<RepositoryPoJo> listByParentId(long parentId, long companyId) {
        log.info("listByParentId:获取父ID下的所有子文件夹[parentId:{}, companyId:{}]", parentId, companyId);
        return list(Wrappers.<RepositoryPoJo>lambdaQuery()
                .eq(RepositoryPoJo::getParentId, parentId)
                .eq(RepositoryPoJo::getCompanyId, companyId));
    }

    @Override
    public boolean update(long id, RepositoryRequest request, long companyId) {
        log.info("update:[request:{}, companyId:{}]", request, companyId);
        String name = request.getName();
        RepositoryPoJo folderPoJo = getByName(name, request.getParentId(), request.getType(), companyId);
        RepositoryPoJo poJo = getById(id);
        if (Objects.isNull(poJo) || poJo.getCompanyId() != companyId) {
            throw CommonException.of(ResCode.REPOSITORY_NOT_EXIST);
        }
        if (poJo.getParentId() == Constant.SYSTEM_ID) {
            throw CommonException.of(ResCode.REPOSITORY_ROOT_NOT_ALLOWED_UPDATE);
        }
        if (Objects.nonNull(folderPoJo) && Objects.equals(folderPoJo.getName(), name)) {
            throw CommonException.of(ResCode.REPOSITORY_NAME_DUPLICATE);
        }
        poJo.setName(name);
        return Objects.nonNull(updateById(poJo));
    }

    @Override
    public boolean delete(long id, long companyId) {
        log.info("delete:[id:{}, companyId:{}]", id, companyId);
        RepositoryPoJo poJo = getById(id);
        if (Objects.isNull(poJo)) {
            return true;
        }
        if (poJo.getParentId() == Constant.SYSTEM_ID) {
            throw CommonException.of(ResCode.REPOSITORY_ROOT_NOT_ALLOWED_DELETE);
        }
        //当前文件夹下有子文件夹不允许删除
        List<RepositoryPoJo> childrenFolder = listByParentId(poJo.getId(), companyId);
        if (!CollectionUtils.isEmpty(childrenFolder)) {
            throw CommonException.of(ResCode.REPOSITORY_EXIST_SUBFOLDER);
        }
        // 当前文件夹下有子文件不允许删除
        List<MaterialPoJo> children = materialService.listByRepositoryId(poJo.getId(), companyId);
        if (!CollectionUtils.isEmpty(children)) {
            throw CommonException.of(ResCode.REPOSITORY_EXIST_FILE);
        }
        return true;
    }


    private long createRoot(int type, long companyId) {
        log.info("createRoot:创建根目录[companyId:{},type:{}]", companyId, type);
        RepositoryPoJo root = RepositoryPoJo.of(type, companyId);
        switch (EnumUtil.of(RepositoryTypeEnum.class, type)) {
            case MATERIAL:
                root.setName("材料仓库");
                break;
            case PRODUCT:
                root.setName("成品仓库");
                break;
            case GARBAGE:
                root.setName("废品仓库");
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + type);
        }
        root.setFullIds(
                Constant.DEFAULT_STRING_SPLIT + Constant.SYSTEM_ID + Constant.DEFAULT_STRING_SPLIT);
        create(root);
        log.info("createRoot:创建根目录完成[{}]", JSON.toJSONString(root));
        return root.getId();
    }


    private List<TreeNode> getNodeList(List<RepositoryPoJo> list) {
        List<TreeNode> nodeList = new ArrayList<>();
        list.forEach(f ->
                nodeList.add(TreeNode.of(f))
        );
        log.info("getNodeList:[nodeList:{}]", nodeList);
        return nodeList;
    }

    private TreeNode getChildren(List<TreeNode> nodeList, TreeNode node) {
        log.info("getChildren:[nodeList:{}, node:{}]", nodeList, node);
        for (TreeNode n : nodeList) {
            long id = node.getId();
            long parentId = n.getParentId();
            log.info("getChildren:[id:{}, parentId:{}]", id, parentId);
            if (id == parentId) {
                node.addNode(getChildren(nodeList, n));
            }
        }
        return node;
    }
}
